#include "syntax.h"

syntax::parseError::parseError(unsigned ln, const char *str) throw():
	lexical::invalidToken(ln){

	std::ostringstream osstream;

	osstream << str;
	osstream << "\nAt line: ";
	osstream << getLine();
	osstream << ".";

	report = osstream.str();

}

syntax::parseError::~parseError() throw(){

	//delete [] report;

}
	
const char *syntax::parseError::what() const throw(){

	return report.c_str();

}

syntax::syntax(lexical *lx):
	lexicalAnalysis(lx), feed(initialFeed), spindle(initialSpindle), relative(false){

	xyzCoordinates[0] = initialX;
	xyzCoordinates[1] = initialY;
	xyzCoordinates[2] = initialZ;
	
}

lexical::token &syntax::getToken(lexical::token &tok)
	throw(reader::bufferOverflow, std::ifstream::failure, parseError){

	try{
		lexicalAnalysis->getToken(tok);
	}
	catch(lexical::invalidToken &e){
		throw parseError(e.getLine(), ERROR_SYMBOL);
	}
	return tok;

}

lexical::token &syntax::nonCoordinates(gObject &object, lexical::token &tok)
	throw(reader::bufferOverflow, std::ifstream::failure, parseError){

	int i = 0;
	bool flag = true;
	gTypeGo &go = *dynamic_cast<gTypeGo *>(object.back());
	
	do{
		switch(tok.symbol){
			case lexical::token::AXIS_X:
				go[0] = atof(tok.lexem.c_str());
				if(relative)
					go[0] += xyzCoordinates[0];
				xyzCoordinates[0] = go[0];
				getToken(tok);
				break;
			case lexical::token::AXIS_Y:
				go[1] = atof(tok.lexem.c_str());
				if(relative)
					go[1] += xyzCoordinates[1];
				xyzCoordinates[1] = go[1];
				getToken(tok);
				break;
			case lexical::token::AXIS_Z:
				go[2] = atof(tok.lexem.c_str());
				if(relative)
					go[2] += xyzCoordinates[2];
				xyzCoordinates[2] = go[2];
				getToken(tok);
				break;
			default:
				if(i == 0)
					throw parseError(tok.line, ERROR_COORDINATES);
				flag = false;
		}
		i++;
	}while(flag);
	return tok;

}

lexical::token &syntax::nonParam(lexical::token &tok)
	throw(reader::bufferOverflow, std::ifstream::failure){

	bool flag = true;

	do{
		switch(tok.symbol){
			case lexical::token::GO_G:
				if(atoi(tok.lexem.c_str()) == TYPE_COORDINATES_REL){
					getToken(tok);
					relative = true;
				}
				else if(atoi(tok.lexem.c_str()) == TYPE_COORDINATES_ABS){
					getToken(tok);
					relative = false;
				}
				else
					flag = false;
				break;
			default:
				flag = false;
				break;
		}
	}while(flag);
	return tok;

}

lexical::token &syntax::nonLinear(gObject &object, lexical::token &tok)
	throw(reader::bufferOverflow, std::ifstream::failure, parseError){

	bool relativeBuff = relative;
	
	nonCoordinates(object, nonParam(tok));
	relative = relativeBuff;
	return tok;

}

bool syntax::evalRadius(gObject &object, float r){

	gTypeGo &go = *dynamic_cast<gTypeGo *>(object[object.size() - 2]);
	gTypeGoCircle &goCircle = *dynamic_cast<gTypeGoCircle *>(object.back());
	float ij[2];
	
	if(!sematic::isValidCircle(go[0], go[1], goCircle[0], goCircle[1], r))
		return false;
	sematic::getIJCoordinates(ij, r, go[0], go[1], goCircle[0], goCircle[1], goCircle.getTypeValue());
	goCircle[3] = ij[0];
	goCircle[4] = ij[1];
	return true;
	
}

lexical::token &syntax::nonRadius(gObject &object, lexical::token &tok)
	throw(reader::bufferOverflow, std::ifstream::failure, parseError){
	
	int i = 0;
	bool flag = true;
	unsigned lineBuff = tok.line;
	gTypeGoCircle &goCircle = *dynamic_cast<gTypeGoCircle *>(object.back());

	do{	
		switch(tok.symbol){
			case lexical::token::RADIUS_R:
				if(!evalRadius(object, atof(tok.lexem.c_str())))
					throw  parseError(tok.line, ERROR_RADIUS);
				getToken(tok);
				break;
			case lexical::token::ARC_I:
				goCircle[3] = atof(tok.lexem.c_str());
				getToken(tok);
				break;
			case lexical::token::ARC_J:
				goCircle[4] = atof(tok.lexem.c_str());
				getToken(tok);
				break;
			case lexical::token::ARC_K:
				goCircle[5] = atof(tok.lexem.c_str());
				getToken(tok);
				break;
			default:
				if(i == 0)
					throw parseError(tok.line, ERROR_CIRCLE);
				flag = false;
		}
		i++;
	}while(flag);
	gTypeGo &prev = *dynamic_cast<gTypeGo *>(object[object.size() - 2]);
	if(!sematic::isValidCircle(
					prev[0], prev[1], 
					goCircle[0], goCircle[1], 
					goCircle[3], goCircle[4]
					)){
		throw parseError(lineBuff, ERROR_RADIUS);
	}
	return tok;

}

lexical::token &syntax::nonArc(gObject &object, lexical::token &tok)
	throw(reader::bufferOverflow, std::ifstream::failure, parseError){

	bool relativeBuff = relative;
	
	nonRadius(object, nonCoordinates(object, nonParam(tok)));
	relative = relativeBuff;
	return tok;

}

lexical::token &syntax::nonOptions(gObject &object, lexical::token &tok)
	throw(reader::bufferOverflow, std::ifstream::failure, parseError){
	
	object.setFeed(feed);
	object.setSpindle(spindle);
	
	if(lexical::token::FEED_F == tok.symbol){
			feed = atof(tok.lexem.c_str());
			object.setFeed(feed);
			getToken(tok);
	}
	if(lexical::token::SPINDLE_S == tok.symbol){
			spindle = atoi(tok.lexem.c_str());
			object.setSpindle(spindle);
			getToken(tok);
	}
	if(lexical::token::GO_G == tok.symbol && TYPE_COORDINATES_ABS == atoi(tok.lexem.c_str())){
		relative = false;
		getToken(tok);
		if(lexical::token::GO_G == tok.symbol){
			int type = atoi(tok.lexem.c_str());
			
			if(type == TYPE_LINEAR_CUT || type == TYPE_LINEAR_MOVE){
				gType *instruction;
				
				instruction = static_cast<gType *>(new gTypeGo(
														type, 
														xyzCoordinates[0], 
														xyzCoordinates[1],
														xyzCoordinates[2]
															));
				object.push_back(instruction);
				nonLinear(object, getToken(tok));
				if(lexical::token::GO_G == tok.symbol && 
					TYPE_COORDINATES_REL == atoi(tok.lexem.c_str())){
					//force relative
					relative = true;
						object.setForceRelative(true);
				}
			}
		}
	}
	return tok;
		
}

lexical::token &syntax::nonControl(gObject &object, lexical::token &tok) 
	throw(reader::bufferOverflow, std::ifstream::failure, parseError){

	unsigned type;
	bool flag = true;
	gType *instruction;
	float buff[3];
	
	buff[0] = xyzCoordinates[0];
	buff[1] = xyzCoordinates[1];
	buff[2] = xyzCoordinates[2];
	do{
		switch(tok.symbol){
			case lexical::token::GO_G:
				type = atoi(tok.lexem.c_str());
				if(type == TYPE_LINEAR_CUT || type == TYPE_LINEAR_MOVE){
					instruction = static_cast<gType *>(new gTypeGo(
															type, 
															xyzCoordinates[0], 
															xyzCoordinates[1],
															xyzCoordinates[2]
															));
					object.push_back(instruction);
					nonLinear(object, getToken(tok));
				}
				else if(type == TYPE_ARC_CLOCK || type == TYPE_ARC_COUNTER){
					if(!object.size()){
						instruction = static_cast<gType *>(new gTypeGo(
														TYPE_LINEAR_MOVE, 
														buff[0], 
														buff[1],
														buff[2]
														));
						object.push_back(instruction);
					}
					instruction = static_cast<gType *>(new gTypeGoCircle(
															type, 
															xyzCoordinates[0], 
															xyzCoordinates[1],
															xyzCoordinates[2],
															0.0, 0.0, 0.0
															));
					object.push_back(instruction);
					nonArc(object, getToken(tok));
				}
				else if(type == TYPE_COORDINATES_ABS){
					relative = false;
					getToken(tok);
				}
				else if(type == TYPE_COORDINATES_REL){
					relative = true;
					getToken(tok);
				}
				else
					getToken(tok);
				break;
			case lexical::token::FEED_F:
				feed = atof(tok.lexem.c_str());
				getToken(tok);
				break;
			case lexical::token::SPINDLE_S:
				spindle = atoi(tok.lexem.c_str());
				getToken(tok);
				break;
			case lexical::token::MACHINE_M:
				getToken(tok);
				break;
			case lexical::token::COMMENT:
				getToken(tok);
				break;
			case lexical::token::TOOL_T:
				getToken(tok);
				break;
			case lexical::token::SUBROUTINE_O:
				getToken(tok);
				break;
			case lexical::token::CALL_P:
				getToken(tok);
				break;
			default:
				flag = false;
				break;
		}
	}while(flag);
	if(object.size() == 1){
		instruction = static_cast<gType *>(new gTypeGo(
													TYPE_LINEAR_MOVE, 
													buff[0], 
													buff[1],
													buff[2]
													));
		object.insert(object.begin(), instruction);
	}
	return tok;

}

gObjectContainer &syntax::getContainer(gObjectContainer &container)
		throw(reader::bufferOverflow, std::ifstream::failure, parseError){

	gObject garbage;
	gObject *object;
	lexical::token tok;
	unsigned long offsetBuff;
	
	do{
		nonControl(garbage, getToken(tok));
                if(tok.symbol == lexical::token::OBJECT_START || tok.symbol == lexical::token::OBJECT_ALTER_START){
			object = new gObject;
                        if(tok.symbol == lexical::token::OBJECT_ALTER_START)
                            object->alter = true;
                        offsetBuff = tok.startOffset;
			container.push_back(object);
			object->setName(tok.lexem);
                        object->setRelative(relative);
			nonControl(*object, nonOptions(*object, getToken(tok)));
			object->setOriginalSpindle(spindle);
			object->setOriginalFeed(feed);
			if(object->size() == 0)
				throw(parseError(tok.line, ERROR_VOID));
                        if((tok.symbol != lexical::token::OBJECT_END && tok.symbol != lexical::token::OBJECT_ALTER_END) || tok.lexem != object->getName())
				throw parseError(tok.line, ERROR_OBJECT);
			object->setOffsets(offsetBuff, tok.endOffset - offsetBuff);
			if(!sematic::isUniqName(container, object->getName()))
				throw parseError(tok.line, ERROR_NAME);
		}
		else if(tok.symbol != lexical::token::END)
			throw parseError(tok.line, ERROR_OBJECT);
	}while(tok.symbol != lexical::token::END);
	
	return container;

}

gObject &syntax::getAllCoords(gObject &object)
	throw(reader::bufferOverflow, std::ifstream::failure, parseError){

	lexical::token tok;
	
	do{
		nonControl(object, getToken(tok));
	}while(tok.symbol != lexical::token::END);
	
	return object;

}
